﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MazeModel
{
    public class RandomCreator
    {

        int mazeHeight = 29;
        int mazeWidth = 40;
        int cellsMade = 0;
        int[,] maze;

        private Random random;
        List<String> visitedCells = new List<String>();

        public RandomCreator()
        {
            this.maze = new int[29,40];
            this.random = new Random();
        }

        // Set the value of maze at location i,j to a path
        void breakWall(int i, int j)
        {
            maze[i, j] = 0;
            visitedCells.Add(hashLocation(i, j));
            cellsMade++;
        }
        void buildWall(int i, int j)
        {
            maze[i, j] = 1;
            visitedCells.Add(hashLocation(i, j));
            cellsMade++;
        }
        // Skilar sellu [i,j] eða 1 ef sellan er ekki til
        public int getCellAt(int i, int j)
        {
            if (i >= 0 && i < this.mazeHeight &&
                j >= 0 && j < this.mazeWidth)
            {
                return maze[i, j];
            }
            else
            {
                return 1;
            }
        }

        // Skilar sellu nálægt [i,j]
        // (1 fyrir ofan, 2 fyrir neðan, 3 til vinstri, 4 til hægri
        public int getCellAdjacent(int i, int j, int dir)
        {
            switch (dir)
            {
                case 1:
                    return this.getCellAt(i - 1, j);
                case 2:
                    return this.getCellAt(i + 1, j);
                case 3:
                    return this.getCellAt(i, j - 1);
                case 4:
                    return this.getCellAt(i, j + 1);
                default:
                    return 1;
            }

        }


        // This is the method that other classes can call on to run the maze constructor
        // It creates a random starting point and adds that as a XX into the constructMaze
        public int[,] constructMaze(int ii, int ij)
        {
            this.maze = new int[ii, ij];

            for (int i = 0; i < ii; i++)
            {
                for (int j = 0; j < ij; j++)
                {
                    this.maze[i, j] = 1;
                }
            }

            this.mazeWidth = maze.GetLength(1);
            this.mazeHeight = maze.GetLength(0);
            int cellQuantity = mazeWidth * mazeHeight;

            this.digTunnel(1, 1);

            maze[1, 1] = 2;
            bool endPlaced = false;

            while (!endPlaced)
            {
                int i = random.Next(this.mazeHeight);
                int j = random.Next(this.mazeWidth);
                if (maze[i, j] == 0)
                {
                    maze[i, j] = 3;
                    endPlaced = true;
                }
            }

            int enemyCount = 0;
            int tryCount = 0;
            while ((enemyCount < 10) && (tryCount < 1000))
            {
                int i = random.Next(this.mazeHeight);
                int j = random.Next(this.mazeWidth-1);
                if ((maze[i, j] == 0) && (maze[i,j+1] == 1))
                {
                    maze[i, j] = 5;
                    enemyCount++;
                }
                tryCount++;
            }

            int diamondCount = 0;
            tryCount = 0;
            while ((diamondCount < 15) && (tryCount < 1000))
            {
                int i = random.Next(this.mazeHeight);
                int j = random.Next(this.mazeWidth);
                if (maze[i, j] == 0)
                {
                    maze[i, j] = 4;
                    diamondCount++;
                }
                tryCount++;
            }


            return maze;
        }

        // Digs a tunnel starting at i,j
        void digTunnel(int ci, int cj)
        {
            int i = ci;
            int j = cj;
            int travel = random.Next(4) + 1;
            int travelEnd = travel;
            travel = nextNumber(travel);

            // Stop if out of bounds
            if ((j <= 0) || (j >= mazeWidth - 1) || (i <= 0) || (i >= mazeHeight - 1))
                return;

            this.breakWall(i, j);

            // Do this for each adjacent cell
            for (; travel != travelEnd; travel = nextNumber(travel))
            {
                i = ci;
                j = cj;
                // Advance i,j to selected random direction
                switch (travel)
                {
                    case 1: i--; break;
                    case 2: i++; break;
                    case 3: j--; break;
                    case 4: j++; break;
                    default: break;
                }

                int wallGauge = 0;
                //Check each direction and add 1 to wallGauge if there's a wall there
                for (int w = 1; w != 5; w++)
                {
                    if ((getCellAdjacent(i, j, w)) == 1)
                    {
                        wallGauge++;
                    }
                }

                if (wallGauge > 2)
                {
                    this.digTunnel(i, j);
                }
                else
                {
                    int rnd = random.Next(100);
                    if (rnd < 15)
                        maze[i, j] = 0;
                }

            }


            /*
            for (; travel != travelEnd; travel = nextNumber(travel))
            {
                // while travel er ekki búið að vera allar 4 áttir
                if (i < 0 || j < 0 || j >= mazeWidth || i >= mazeHeight)
                {
                    i = 3;
                    j = 3;
                }

                if (travel == 0)
                {
                    j--;
                }
                else if (travel == 1)
                {
                    j++;
                }
                else if (travel == 2)
                {
                    i--;
                }
                else
                {
                    i++;
                }

                if (!this.checkCell(i, j))
                {

                    int? left = ((j > 0) && (j <= mazeWidth - 1) && (i >= 0) && (i <= mazeHeight - 1)) ? (int?)maze[i, j - 1] : null;
                    int? right = ((j >= 0) && (j < mazeWidth - 1) && (i >= 0) && (i <= mazeHeight - 1)) ? (int?)maze[i, j + 1] : null;
                    int? up = ((j >= 0) && (j <= mazeWidth - 1) && (i > 0) && (i <= mazeHeight - 1)) ? (int?)maze[i - 1, j] : null;
                    int? down = ((j >= 0) && (j <= mazeWidth - 1) && (i >= 0) && (i < mazeHeight - 1)) ? (int?)maze[i + 1, j] : null;

                    if (left != null
                        && right != null
                        && up != null
                        && down != null
                        && left + right + down + up > 0)
                    {
                        breakWall(i, j);
                    }
                    else
                    {
                        buildWall(i, j);
                    }
                }
            }
            */
        }

        public static int nextNumber(int i)
        {
            if (i < 4)
                return i + 1;
            return 1;
        }

        string hashLocation(int i, int j)
        {
            return Convert.ToString(i) + Convert.ToString(j);
        }

        bool checkCell(int i, int j)
        {
            if (visitedCells.Contains(hashLocation(i, j)))
                return true;
            return false;
        }
    }
}
